package com.java.concurrent;

import java.util.ArrayList;
import java.util.List;

/**
 * @program: java_basic_knowledge
 * @description:
 * @author: CaoYong
 * @create: 2021-04-22 09:40
 **/
public class ThreadKnowledge {


    // 第一种创建线程的方法，即继承Thread类并重写run()方法，但是由于Java只能单继承如果已经继承了其他类就不能再继承Thread
    public static class ExtendThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello:" + currentThread().getName());
        }
    }

    // 第二种创建线程的方法，实现Runnable接口并重写run()方法
    public static class ImplementsRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("Hello:");
        }
    }

    public static class HowToDefineThread {

        private int priority;

        public final static int MIN_PRIORITY = 1;

        public final static int NORMAL_PRIORITY = 5;

        public final static int MAX_PRIORITY = 10;

        private volatile int threadStatus = 0;

        private boolean daemon = false; // 是否是守护线程， 守护线程是其他线程的辅助线程，当其他线程运行结束时，其存在就无意义了，也会停止运行

        public enum State{
            NEW, // 没有调用start()的线程状态为new
            RUNNABLE, // 调用start()后线程在执行run()方法且没有阻塞时的状态为RUNNABLE, 不过RUNNABLE并不代表CPU一定正在执行这段代码，它是就绪态和执行态的合并
            BLOCKED,
            WAITING,
            TIMED_WAITING,
            TERMINATED; // 线程执行结束
        }

        public int getPriority() {
            return priority;
        }

        public void setPriority(int priority) {
            this.priority = priority;
        }



        public final boolean isAlive() {
            return true;
        }

        // 静态的sleep方法，可以让当前线程睡眠指定时间，此时该线程会让出CPU, 睡眠期间线程可以被中断，中断后悔抛出InterruptedException
        public static void sleep(long millis) {

        }

        // 静态的让出方法，告诉操作系统的调度器，现在不着急用CPU,可以让其他线程先运行, 不过这个方法只是对调度器提出建议，调度器是否处理由操作系统自行决定
        public static void yield() {}

        // 等待方法，让调用join()方法的线程等待该线程的结束，在等待过程中线程可能会被中断，如果中断则抛出InterruptedException,join()方法也可以指定等待时间
        public void join() {}

    }

    public static class ShareMemeoryDemo {
        private static int shared = 0;
        private static void incrShared() {
            shared ++;
        }

        static class ChildThread extends Thread {
            List<String> list;

            public ChildThread(List<String> list) {
                this.list = list;
            }

            @Override
            public void run() {
                incrShared();
                list.add(Thread.currentThread().getName());
            }
        }

        static class CounterThread extends Thread {

            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    incrShared();
                }
            }
        }
    }

    public static class VisibleDemo {
        public static boolean shutdown = false;
        static class VisibleThread extends Thread {
            @Override
            public void run() {
                while (!shutdown) {

                }
                System.out.println("exit visible");
            }
        }
    }

    public static void testShareMemory() throws Exception{
//        List<String> list = new ArrayList<>();
//        Thread thread1 = new ShareMemeoryDemo.ChildThread(list);
//        Thread thread2 = new ShareMemeoryDemo.ChildThread(list);
//
//        thread1.start();
//        thread2.start();
//        thread1.join();
//        thread2.join();
//
//        System.out.println(ShareMemeoryDemo.shared);
//        System.out.println(list);

        // 执行结果并不为1000000, 并且每次的执行结果都不相同，这是因为产生了竞态条件,即当多个线程访问同一个对象时，最终执行结果与执行时序有关，可能正确也可能不正确
        int num = 1000;
        Thread[] threads = new Thread[num];
        for (int i = 0; i < num; i++) {
            threads[i] = new ShareMemeoryDemo.CounterThread();
            threads[i].start();
        }

        for (int i = 0; i < num; i++) {
            threads[i].join();
        }
        System.out.println(ShareMemeoryDemo.shared);
    }

    // 内存可见性问题， 多个线程可以共享访问和操作相同的变量，但一个线程对一个共享变量的修改。另一个线程不一定能马上就看到，甚至永远不可能看到
    // 在计算机系统中，数据除了存储在内存中，数据也有可能缓存在CPU寄存器和各级缓存中，当访问以后变量时，可能直接从寄存或者CPU缓存中获取，而不是内存
    // 当修改一个变量时也有可能是先写到缓存然后再同步更新到内存
    // 在多CPU的多线程场景下，可能出现一个线程对内存的修改另一个线程看不到，要么是修改没有第一时间同步到内存，要么就是另一个线程就没有从内存中读
    public static void testVisibility() throws Exception{
        VisibleDemo.VisibleThread visibleThread = new VisibleDemo.VisibleThread();
        visibleThread.start();
        Thread.sleep(1000);
        VisibleDemo.shutdown = true;
        System.out.println("exit main");
    }


    public static void howToUseThread() {
        ExtendThread extendThread = new ExtendThread();
        extendThread.start(); // 调用start()执行线程
        Thread implementsRunnable = new Thread(new ImplementsRunnable()); // 以runnable接口的实现类为参数创建Thread
        implementsRunnable.start();
        System.out.println(implementsRunnable.getName());
    }

    public static void main(String[] args) throws Exception{
//        howToUseThread();
        testShareMemory();
//        testVisibility();
    }
}
